/*
	Instruction decoder
*/

module decoder
(
	input	wire	[31:0]	instr,
	
	// common signal
	output	wire	[02:0]	func,
	output	reg	    [31:0]	imm,
	output	wire			operandSel1,				// 0:regFileData1 , 1:pc
	output	wire			operandSel2,				// 0:regFileData2 , 1:imm
    // register file control
	output	wire	[04:0]	gprId1,
	output	wire	[04:0]	gprId2,
	
	// ALU control
	output  wire            aluriOp,
	output	wire	[04:0]	shamt,
	output	wire			aluFuncSel,

	
	// Write back control
	output	wire			gprWriteBackEn,
	output	wire	[04:0]	gprWriteBackId,
	output	wire			gprWriteBackFromALU,
	output	wire			gprWriteBackFromSpecialAdder,
	output	wire			gprWriteBackFromPcPlus4,
	output	wire			gprWriteBackFromLSU,
	
	// jump & branch
	output	wire			branchOp,
	output	wire	[00:0]	jumpFlag,
    output  wire    [31:0]  branchOffset,
	
	// LSU & pcReg control
    output	wire			loadOp,
	output	wire			storeOp
														
);
	parameter	LUI 	= 7'b0110111,
				AUIPC 	= 7'b0010111,
				JAL 	= 7'b1101111,
				JALR	= 7'b1100111,
				BRANCH 	= 7'b1100011,
				ALURI 	= 7'b0010011,
				ALURR 	= 7'b0110011,
				LOAD	= 7'b0000011,
				STORE 	= 7'b0100011,
				FENCE	= 7'b0001111,
				SYSTEM 	= 7'b1110011;

	
	// common control
	assign	func			= instr[14:12];

	// regFile control
	assign	gprId1		= ( instr[06:00] == LUI ) 	? 5'b00000 	: instr[19:15];
	assign	gprId2		= ( instr[24:20] );
	// write back control
	assign	gprWriteBackId		= instr[11:07];
	assign	gprWriteBackEn		= !( instr[06:00] == BRANCH	|| instr[06:00] == STORE || instr[06:00] == FENCE	|| instr[06:00] == SYSTEM);
	
	assign	gprWriteBackFromALU				= ( instr[06:0] == ALURI || instr[06:0] == ALURR 	); 
	assign	gprWriteBackFromSpecialAdder	= ( instr[06:0] == LUI 	|| instr[06:0] == AUIPC 	);
	assign	gprWriteBackFromPcPlus4			= ( instr[06:0] == JAL 	|| instr[06:0] == JALR  	);
	assign	gprWriteBackFromLSU				= ( instr[06:0] == LOAD 							);
	
	// ALU control
	assign	shamt 			= instr[24:20];				// Shift number
	assign	aluFuncSel      = instr[30];					// Operation qualification (+/-, Logical/Arithmetic)
	assign  aluriOp         = instr[06:0] == ALURI;
	// OperandSel1
	assign	operandSel1     =  ( instr[6:0] == AUIPC || instr[6:0] == JAL );

	// OperandSel2
	assign	operandSel2     = !( instr[6:0] == ALURR || instr[6:0] == BRANCH );

	
	// immGen
	wire [31:0] immI = {{21{instr[31]}}, instr[30:20]};
	wire [31:0] immS = {{21{instr[31]}}, instr[30:25], instr[11:7]};
	wire [31:0] immB = {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0};
	wire [31:0] immJ = {{12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0};   
	wire [31:0] immU = {instr[31], instr[30:12], {12{1'b0}}};
    
	always @ ( * ) begin
        case ( instr[06:0] )
            LUI     : imm = immU;
            AUIPC   : imm = immU;
            JAL     : imm = immJ;
            JALR    : imm = immI;
            ALURI   : imm = immI;
            LOAD    : imm = immI;
            BRANCH  : imm = immB;
            STORE   : imm = immS;
            default : imm = 32'bx;
        endcase
    end
    // jump & branch signal
	assign	jumpFlag		= instr[06:0] == JAL || instr[06:0] == JALR;
    assign  branchOffset    = immB;
	assign	branchOp		= instr[06:0] == BRANCH;


            
	
	// LSU & pcReg control
	assign	loadOp		= instr[06:0] == LOAD;
	assign	storeOp		= instr[06:0] == STORE;
	
endmodule